package xyz.scootaloo.kami.cloud.util

import io.vertx.core.MultiMap
import io.vertx.core.http.HttpServerRequest
import io.vertx.core.http.HttpServerResponse
import io.vertx.core.json.JsonObject
import io.vertx.ext.web.RoutingContext
import xyz.scootaloo.kami.cloud.handler.queryMillisSeconds
import xyz.scootaloo.kami.cloud.handler.remoteIpAddress
import xyz.scootaloo.kami.cloud.lang.Constant
import xyz.scootaloo.kami.cloud.lang.Global

/**
 * @author flutterdash@qq.com
 * @since 2022/4/14 10:41
 */
object RequestReporter {

    /**
     * $$-------------------BEGIN--------------------$$
     * $$------------------Request-------------------$$
     * >> Profile
     * URI    : /account/register
     * Remote : 127.0.0.1:4200
     * Method : POST
     *
     * >> Headers
     * Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng
     * Accept-Encoding: gzip, deflate, br
     * Accept-Language: zh-CN,zh;q=0.9
     *
     * >> Body: application/json
     * {
     *     "username": "root",
     *     "password": "admin"
     * }
     *
     * $$-----------------Response-------------------$$
     * >> Profile
     * Status  : 200
     * Interval: 360ms (10:42:43.356, 10:42:43.716)
     *
     * >> Headers
     * content-length: 100
     * content-type: application/json
     *
     * >> Body: application/json
     * {
     *     "username": "root",
     *     "password", "admin"
     * }
     *
     * $$--------------------END---------------------$$
     */
    fun fullReport(ctx: RoutingContext): String {
        val request = ctx.request()
        val buff = StringBuilder()
        buff.append('\n').append(
            """
            $PROMPT_BEG
            $PROMPT_REQ
            Uri    : ${ctx.request().uri()}
            Remote : ${ctx.remoteIpAddress()}
            Method : ${ctx.request().method()}
            
            >> Headers
            ${requestHeaders(request)}
            """.trimIndent()
        )
        if (isAcceptContentType(request.headers())) {
            buff.append('\n').append(
                """
                >> Body
                ${ctx.bodyAsString}
                """.trimIndent()
            )
        }

        val response = ctx.response()
        buff.append('\n').append(
            """
            $PROMPT_RES
            Status   : ${response.statusCode}
            Interval : ${ctx.queryMillisSeconds()}ms
            
            >> Headers
            ${responseHeaders(response)}
            
            """.trimIndent()
        )

        if (Global.APP_LOG_CONF.logRequestWhitResponse && isAcceptContentType(response.headers())) {
            buff.append('\n').append(
                """
                >> Body
                ${JsonObject.mapFrom(ctx.get(Constant.KEY_HTTP_RESP_STORE))}
                """.trimIndent()
            )
        }

        buff.append(PROMPT_END)

        return buff.toString().outputFormat()
    }

    /**
     * 192.168.1.112 POST 22ms /account/register
     */
    fun simpleRequestReport(ctx: RoutingContext): String {
        var line = "${ctx.remoteIpAddress()} ${ctx.request().method()} ${ctx.queryMillisSeconds()}ms ${ctx.request().uri()}"
        if (Global.APP_LOG_CONF.logRequestWhitResponse && isAcceptContentType(ctx.request().headers())) {
            val reqBody = ctx.bodyAsString
            line += " => $reqBody"
        }
        return line
    }

    private fun requestHeaders(request: HttpServerRequest): String {
        val buff = StringBuilder()
        for ((k, v) in request.headers()) {
            buff.append("$k: $v\n")
        }
        return buff.toString()
    }

    private fun responseHeaders(response: HttpServerResponse): String {
        val buff = StringBuilder()
        for ((k, v) in response.headers()) {
            buff.append("$k: $v\n")
        }
        return buff.toString()
    }

    private fun isAcceptContentType(headers: MultiMap): Boolean {
        val contentType = headers["Content-Type"] ?: headers["content-type"]
        contentType ?: return false
        return contentType.indexOf("json") != -1 || contentType.indexOf("xml") != -1
    }

    private fun String.outputFormat(): String {
        val res = StringBuilder()
        for (line in lines()) {
            res.appendLine(line.trimStart())
        }
        return res.toString()
    }

    private const val PROMPT_BEG = "$$-------------------BEGIN--------------------$$"
    private const val PROMPT_REQ = "$$------------------Request-------------------$$"
    private const val PROMPT_RES = "$$-----------------Response-------------------$$"
    private const val PROMPT_END = "$$--------------------END---------------------$$"
}